package com.qc.sys.filter;

import com.qc.common.config.Global;
import com.qc.common.exception.GlobalException;
import com.qc.common.exception.ValidateCodeException;
import com.qc.common.mapper.JsonMapper;
import com.qc.common.servlet.ValidateCodeServlet;
import com.qc.common.utils.IdGen;
import com.qc.common.utils.StringUtils;
import com.qc.sys.security.UsernamePasswordToken;
import com.qc.sys.utils.UserUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * 自定义表单验证（包含验证码）过滤类，兼容ajax 和移动端请求
 */
@Service
public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {

	public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";
	public static final String DEFAULT_MOBILE_PARAM = "mobileLogin";
	public static final String DEFAULT_MESSAGE_CODE = "messageCode";

	private String captchaParam = DEFAULT_CAPTCHA_PARAM;
	private String mobileLoginParam = DEFAULT_MOBILE_PARAM;
	private String messageCode = DEFAULT_MESSAGE_CODE;

	protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
		String username = getUsername(request);
		String password = getPassword(request);
		if (password==null){
			password = "";
		}
		boolean rememberMe = isRememberMe(request);
		String host = StringUtils.getRemoteAddr((HttpServletRequest)request);
		String captcha = getCaptcha(request);
		boolean mobile = isMobileLogin(request);
		return new UsernamePasswordToken(username, password.toCharArray(), rememberMe, host, captcha, mobile);
	}
	
	/**
	 * 获取记住我
	 */
	@Override
	protected boolean isRememberMe(ServletRequest request) {
		String isRememberMe = WebUtils.getCleanParam(request, getRememberMeParam());
		if (StringUtils.isBlank(isRememberMe)){
			isRememberMe = StringUtils.toString(request.getAttribute(getRememberMeParam()), StringUtils.EMPTY);
		}
		return StringUtils.toBoolean(isRememberMe);
	}

	public String getCaptchaParam() {
		return captchaParam;
	}

	protected String getCaptcha(ServletRequest request) {
		return WebUtils.getCleanParam(request, getCaptchaParam());
	}

	public String getMobileLoginParam() {
		return mobileLoginParam;
	}
	
	protected boolean isMobileLogin(ServletRequest request) {
        return WebUtils.isTrue(request, getMobileLoginParam());
    }

	public  String getMessageCode() {
		return messageCode;
	}

	@Override
	protected void issueSuccessRedirect(ServletRequest request,
			ServletResponse response) throws Exception {
		WebUtils.issueRedirect(request, response, getSuccessUrl(), null, true);
	}

	/**
	 * 是否是ajax请求
	 * @param httpServletRequest
	 * @return true & false
	 */
	private boolean isAjaxRequest(HttpServletRequest httpServletRequest){
		return !(!(httpServletRequest.getHeader("accept").contains("application/json") || (httpServletRequest.getHeader("X-Requested-With") != null && httpServletRequest
				.getHeader("X-Requested-With").contains("XMLHttpRequest"))));
	}
	/**
	 *拒绝访问时
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	@Override
	protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
		if (isLoginRequest(request, response)) {
			if (isLoginSubmission(request, response)) {
				return executeLogin(request, response);
			} else {
				return true;
			}
		} else {
			/*
			*如果是移动端，提示重新登录
			* */
			boolean mobile = WebUtils.isTrue(request, DEFAULT_MOBILE_PARAM);
			if (mobile) {
				renderString(response,"1005",null,"");
			}else
				saveRequestAndRedirectToLogin(request, response);
			return false;
		}
	}

	/**
	 * 授权成功， false不继续处理，true继续处理
	 * @param token
	 * @param subject
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	@Override
	protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
		HttpServletRequest httpServletRequest = (HttpServletRequest) request;
		boolean mobile = WebUtils.isTrue(request, DEFAULT_MOBILE_PARAM);
		if (isAjaxRequest(httpServletRequest)||mobile) { //异步请求与移动端请求
			/**
			 * content 可以根据需求返回User账户的信息，这里暂不返回
			 * */
			renderString(response,"0",null,"");
			return false;
		}else{
			return super.onLoginSuccess(token, subject, request, response);
		}
	}

	/**
	 * 登录失败调用事件
	 */
	@Override
	protected boolean onLoginFailure(AuthenticationToken token,AuthenticationException e, ServletRequest request, ServletResponse response) {
		String className = e.getClass().getName(),
				messageCode = "",msgContent = null;
		if (IncorrectCredentialsException.class.getName().equals(className)
				|| UnknownAccountException.class.getName().equals(className)){
			messageCode ="1001";
		}
		else if (ValidateCodeException.class.getName().equals(className)){
			messageCode ="1002";
			// 验证失败清空验证码
			UserUtils.getSession().setAttribute(ValidateCodeServlet.VALIDATE_CODE, IdGen.uuid());
		}
		else if(AuthenticationException.class.getName().equals(className)){
			msgContent = e.getMessage();
		}else{
			messageCode ="1004";
		}

		String username = WebUtils.getCleanParam(request, FormAuthenticationFilter.DEFAULT_USERNAME_PARAM);

		// 非授权异常，登录失败，验证码加1。
		if (!UnauthorizedException.class.getName().equals(className)){
			UserUtils.isValidateCodeLogin(username, true, false);
		}

		renderString(response,messageCode,msgContent,"");
		return false;
	}

	/**
	 * 客户端返回字符串
	 * @param response
	 * @param content
	 * @return
			 */
	private void renderString(ServletResponse response,String msgCode,String msg,Object content) {
		try {
			String respContent =  JsonMapper.getInstance().toRespEntityString(msgCode, msg==null?Global.getExceptionInfo(msgCode):msg,content);
			response.reset();
			response.setContentType("text/html;charset=UTF-8");
			response.setCharacterEncoding("UTF-8");
			response.getWriter().print(respContent);
		} catch (IOException e) {

		}
	}
}